package com.cy.aviator;

import com.googlecode.aviator.*;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.googlecode.aviator.runtime.JavaMethodReflectionFunctionMissing;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//https://www.yuque.com/boyan-avfmj/aviatorscript
public class DemoWithAviator {

	@Test(description = "表达式执行")
	public void t0() throws IOException {
		// 返回一个全局共享的 AviatorScript 引擎,没有定制化的需求,
		Expression script = AviatorEvaluator.getInstance().compile("1+1");
		Object execute = script.execute();
		System.out.println(execute.getClass());
		System.out.println(execute);
	}
	@Test(description = "文件执行 有很多例子 https://github.com/killme2008/aviatorscript")
	public void t1() throws IOException {
		Expression exp = AviatorEvaluator.getInstance().compileScript("aviator/hello.av");
		exp.execute();
	}
	@Test(description = "表达式缓存")
	public void t2() throws IOException {
		AviatorEvaluator.getInstance().compile("1+1", true);
		// 默认通过表达式来缓存 如果表达式太长可以自定义缓存key
		AviatorEvaluator.getInstance().compile("我是缓存key", "1+1", true);
		// 全局设置
		AviatorEvaluator.getInstance().setCachedExpressionByDefault(true);
		AviatorEvaluator.getInstance().compile("1+1");
	}
	@Test(description = "变量入参")
	public void t3() {
		String expression = "a-(b-c) > 100";
		Expression compiledExp = AviatorEvaluator.compile(expression, true);
		// AviatorEvaluator.getInstance().compile (等同) AviatorEvaluator.compile
		// 性能会好于MAP
		System.out.println(compiledExp.execute(compiledExp.newEnv("a", 100.3, "b", 45, "c", -199.100)));

		System.out.println(AviatorEvaluator.compile("name != nil ? ('hello, ' + name):'who are u?'").execute());
		System.out.println(AviatorEvaluator.compile("name != nil ? ('hello, ' + name):'who are u?'").execute(compiledExp.newEnv("name", "dennis")));
	}
	@Test(description = "校验")
	public void t4() {
		String line = "1 +* 2";
		AviatorEvaluator.validate(line);
	}
	@Test(description = "校验 批量")
	public void t5() throws IOException {
		List<String> lines = FileUtils.readLines(new File("D://1.log"), "utf8");
		lines.forEach(x -> {
			try {
				AviatorEvaluator.validate(x);
			} catch (Exception e) {
				System.out.println("error " + x);
			}
		});

	}
	@Test(description = "引擎模式")
	public void t6() {
		// 完整的参数 设置列表
		// https://www.yuque.com/boyan-avfmj/aviatorscript/yr1oau

		// AviatorEvaluator.EVAL,默认值,以运行时的性能优先,编译会花费更多时间做优化,目前会做一些常量折叠、公共变量提取的优化。适合长期运行的表达式。
		// AviatorEvaluator.COMPILE,以编译的性能优先,不会做任何编译优化,牺牲一定的运行性能,适合需要频繁编译表达式的场景。
		AviatorEvaluator.getInstance().setOption(Options.OPTIMIZE_LEVEL, AviatorEvaluator.EVAL);
	}
	@Test(description = "基本类型 long bigInt double decimal")
	public void t7() {
		// 整数都会被转换为long 类型 也支持BigInt  long + long 溢出会有问题 除非 和一个 bigint组合计算
		// let b = 1000N; bigint 以N结尾
		String script = "let a = 10223372036854774807;let b = 1000N;a+b";
		System.out.println(AviatorEvaluator.getInstance().compile(script).execute());

		// 浮点数 double 比如 1.34159265 0.33333 科学计数法表示 如 1e-2 2E3 等等 大小写字母 e 皆可

		// 高精度计算(Decimal) 只要浮点数以 M 结尾就会识别类型为 deicmal 例如 1.34M 0.333M 或者科学计数法 2e-3M 等等

		// 除了 double 以外的数字类型和 decimal 一起运算,结果为 decimal 任何有 double 参与的运算,结果都为 double

		// 希望所有浮点数都解析为 decimal,可以开启 Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMA
		// 多种类型参与的运算,按照下列顺序 long -> bigint -> decimal -> double  自动提升
		// 比如 long 和 bigint 运算结果为 bigint, long 和 decimal 运算结果为 decimal,任何类型和 double 一起运算结果为 double

		// 你可以通过 long(x) 将一个数字强制转化为 long,这个过程中可能丢失精度,也可以用 double(x) 将一个数字强转为 double 类型
		System.out.println(AviatorEvaluator.execute("decimal(1)+1").getClass());
	}
	@Test(description = "基本类型 String")
	public void t8() {
		// 字符串 和java类似 可以用 "a" 或者 'a'都可
		// 常用函数 https://www.yuque.com/boyan-avfmj/aviatorscript/ashevw
		System.out.println(AviatorEvaluator.execute("string.length('a'+1)"));
		// 转义 换行
		AviatorEvaluator.execute("p('AviatorScript is great.\\r\\nLet\\'s try it!')");
		// 字符串插值 替换占位符
		// 字符串中 #{} 括起来的表达式都将在当前上下文里自动执行求值,然后插入到最终的结果字符串,上面的例子将输出
		AviatorEvaluator.execute("let name = 'aviator';let a = 1;let b = 2;let s = 'hello, #{name}, #{a} + #{b} = #{a + b}';p(s);");
	}
	@Test(description = "基本类型 boolean 三元运算符 ")
	public void t9() throws IOException {
		// 所有的逻辑运算符
		// AviatorEvaluator.getInstance().compileScript("aviator/boolean1.av").execute();
		// && 和 || 都支持短路规则
		AviatorEvaluator.execute("let a = false && println('not reachable');p(a)");
		AviatorEvaluator.execute("let b = true || println('not reachable') ;p(b)");

		// 三元运算符 返回值可以不同
		AviatorEvaluator.execute("let c = 1>3?true:'haha';p(c)");
	}
	@Test(description = "正则表达式")
	public void t10() throws IOException {
		// 我们定义了一个正则表达式 p,用于匹配以 .av 结尾的文件名,匹配是用 =~ 运算符,匹配运算符左侧是字符串,右侧是正则表达式,如果匹配成功,返回 true,否则返回 false。
		// 这里 regexp.av 是匹配成功,因此打印 true , 如果匹配成功,同时 AviatorScript 会将正则中的匹配的分组放入 $0 , $1 , $2  ... 的变量中,其中 $0  表示匹配的整个字符串,而 $1 表示第一个分组,以此类推。这里就是文件名,正则中用括号括起来的第一个分组 (.*) 。
		System.out.println(AviatorEvaluator.getInstance().compileScript("aviator/zhengze1.av").execute());
	}
	@Test(description = "幂运算")
	public void t11() {
		// 幂运算的基本规则：
		// 基数和指数都为 long,并且指数为正数,结果为 long
		// 基数和指数都为 long,并且指数为负数,结果为 double
		// 基数为 decimal,指数取整数部分(int value),等价于 BigDecimal#pow(int) 。
		// 基数为 bigint,指数取整数部分(int value),等价于 BigInteger#pow(int) 。
		// 基数或者指数任一为 double,结果为 double
		AviatorEvaluator.execute("p(2 ** 3);");
		AviatorEvaluator.execute("p(2 ** -3);");
		AviatorEvaluator.execute("p(2N ** 3);");
		AviatorEvaluator.execute("p(2M ** 3);");
		AviatorEvaluator.execute("p(2 ** 2.2);");
	}
	@Test(description = "位运算")
	public void t12() throws IOException {
		// &  位与运算
		// |  或运算
		// ^  异或运算
		// ~  一元非运算
		// << 左移运算
		// >> 右移运算
		// >>> 无符号右移
		AviatorEvaluator.getInstance().compileScript("aviator/bit1.av").execute();
		System.out.println(Integer.parseInt("1", 16));
		System.out.println(Integer.parseInt("11", 2));
	}
	@Test(description = "自定义运算符")
	public void t13() {
		// 你也可以自定义任意运算符的行为，比如我们想将整数相除的结果修改为浮点数
		// https://www.yuque.com/boyan-avfmj/aviatorscript/ydllav
		AviatorEvaluator.getInstance().addOpFunction(OperatorType.DIV, new AbstractFunction() {
			@Override
			public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
			                          final AviatorObject arg2) {
				if (arg1.getAviatorType() == AviatorType.Long
						&& arg2.getAviatorType() == AviatorType.Long) {
					// If arg1 and arg2 are all long type.
					// Cast arg2 into double and divided by arg1.
					double d = FunctionUtils.getNumberValue(arg1, env).longValue()
							/ FunctionUtils.getNumberValue(arg2, env).doubleValue();
					return AviatorDouble.valueOf(d);
				} else {
					// Otherwise, call aviatorscript's div function.
					return arg1.div(arg2, env);
				}
			}

			@Override
			public String getName() {
				return OperatorType.DIV.getToken();
			}
		});
		System.out.println(AviatorEvaluator.execute("2/1").getClass());
		// AviatorLong 表示整数 long 类型
		// AviatorDouble 表示浮点数 double 类型
		// AviatorBigInt 表示 bigint 大整数
		// AviatorDecimal 表示 decimal 类型
		// AviatorString 表示字符串
		// AviatorPattern  表示正则表达式
		// AviatorNil 特殊类型 nil，将在下一节变量中讲到
		// AviatorJavaType 表示变量，将在下一节讲到
	}
	@Test(description = "变量")
	public void t14() throws IOException {
		AviatorEvaluator.getInstance().compileScript("aviator/let1.av").execute();
		// AviatorScript 是动态类型语言，变量的类型随着赋值而改变
	}
	@Test(description = "null nil")
	public void t15() throws IOException {
		// 在 AviatorScript 中， nil 可以参与所有的比较运算符，只是规定任何类型都比nil大除了nil本身：
		AviatorEvaluator.getInstance().compileScript("aviator/nil.av").execute();
	}
	@AllArgsConstructor
	@Getter
	@Setter
	private static class TestAviator {
		int i;
		float f;
		String[] strs;
	}
	@Test(description = "变量的语法糖  支持语法糖嵌套")
	public void t16() {
		Map<String, Object> env = new HashMap<>();
		env.put("foo", new TestAviator(100, 3.14f, new String[]{"a", "b", "c"}));
		System.out.println(AviatorEvaluator.execute("'foo.i = '+foo.i", env));
		System.out.println(AviatorEvaluator.execute("'foo.f = '+foo.f", env));
		System.out.println(AviatorEvaluator.execute("'foo.strs[1] = '+ (foo.strs[1])", env));

		System.out.println(AviatorEvaluator.execute("foo.i = 100; foo.f + foo.i", env));
	}
	@Test(description = "访问静态变量")
	public void t17() {
		// 对于 java.lang 下面的类可以直接访问，比如 Math 等，但是对于其他包下面的类，都要求先用 use 语法导入该类到当前上下文
		AviatorEvaluator.execute("p('Math.PI is:' + Math.PI);");
		AviatorEvaluator.execute("use  com.googlecode.aviator.AviatorEvaluator;" +
				"p('AviatorEvaluator.VERSION is: ' + AviatorEvaluator.VERSION);" +
				"p('AviatorEvaluator.COMPILE is: ' + AviatorEvaluator.COMPILE);");
	}
	@Test(description = "特殊变量")
	public void t18() {
		AviatorEvaluator.execute("let a = 1; p(__env__)");
	}
	@Test(description = "获取表达式中未赋值的变量")
	public void t19() {
		System.out.println(AviatorEvaluator.compile("let c=b + a + k.b;").getVariableNames());
		System.out.println(AviatorEvaluator.compile("let c=b + a + k.b;").getVariableFullNames());
	}
	@Test(description = "变量作用域  是否写let的含义")
	public void t20() throws IOException {
		//let 语句就是让你在特定作用域内定义一个变量，如果父作用域有同名的变量，将“掩盖”父作用域的变量。如果不使用 let ，你读写的将仍然是父作用域的变量：

		// 作用域还可以继续深层嵌套，遵循的规则不变：
		// let 定义当前作用域的变量，这些变量同时可以被它的子作用域访问和修改，离开当前作用域后不可触达。
		// let 定义的变量将“掩盖”父作用域的同名变量。
		// 子作用域可以访问和修改父作用域定义的变量，离开子作用域后修改继续生效。
		AviatorEvaluator.getInstance().compileScript("resource/let2.av");
		AviatorEvaluator.getInstance().compileScript("resource/let3.av");
		AviatorEvaluator.getInstance().compileScript("resource/let4.av");
		AviatorEvaluator.getInstance().compileScript("resource/let5.av");
	}
	@Test(description = "到底什么时候return")
	public void t21() {
		// 如果你想返回表达式的值，而不是为 nil，最后一个表达式不加分号即可
		System.out.println(AviatorEvaluator.execute("let a=1;a+1;"));
		System.out.println(AviatorEvaluator.execute("let a=1;a+1"));
		System.out.println(AviatorEvaluator.execute("let a=1;return a+1;"));

		Expression expression = AviatorEvaluator.compile("if a<b {return 'a<b';} return a-b;");
		// 注意到，这里我们用 Expression#newEnv(key1, value1, key2, value2 ...) 的方式来创建执行的的 context 环境，这是推荐的方式，性能会比直接构造一个 HashMap 略好。
		System.out.println(expression.execute(expression.newEnv("a", 1, "b", 9)));
		System.out.println(expression.execute(expression.newEnv("a", 9, "b", 1)));
	}
	@Test(description = "Feature ??????")
	public void t22() {
		AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
		instance.setOption(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY, false);
		instance.setOption(Options.FEATURE_SET,
				Feature.asSet(Feature.Assignment,
						Feature.ForLoop,
						Feature.WhileLoop,
						Feature.Lambda,
						Feature.Let));

		System.out.println(instance
				.execute("let square = lambda(x) -> x*2 end; for x in range(0, 10) { p(square(x)); }"));
	}
	@Test(description = "创建对象")
	public void t23() {
		// 启用基于反射的方法查找和调用 性能特别差 误用
		AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
		AviatorEvaluator.execute(
				"let d = new java.util.Date();" +
						"p(type(d));" +
						"p(d);" +
						"p(getYear(d));" +
						"p(getMonth(d));"

		);
	}
	@Test(description = "use 语句引用 Java 类")
	public void t24() throws IOException {
		// 简单
		AviatorEvaluator.getInstance().compileScript("aviator/java1.av").execute();
		// 复杂
		AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
		AviatorEvaluator.getInstance().compileScript("aviator/java2.av").execute();
	}
	@Test(description = "if 的使用 elseif  else")
	public void t25() throws IOException {
		AviatorEvaluator.getInstance().compileScript("aviator/if1.av");
		AviatorEvaluator.getInstance().compileScript("aviator/if2.av");
		AviatorEvaluator.getInstance().compileScript("aviator/if3.av");
	}
	@Test(description = "遍历 for while break return")
	public void t26() {
		// 具体看这个https://www.yuque.com/boyan-avfmj/aviatorscript/vfqgqn
	}
	@Test(description = "try catch finally")
	public void t27() {
		// https://www.yuque.com/boyan-avfmj/aviatorscript/sikgal
	}

	static class AddFunction extends AbstractFunction {
		@Override
		public AviatorObject call(Map<String, Object> env,
		                          AviatorObject arg1, AviatorObject arg2) {
			Number left = FunctionUtils.getNumberValue(arg1, env);
			Number right = FunctionUtils.getNumberValue(arg2, env);
			return new AviatorDouble(left.doubleValue() + right.doubleValue());
		}
		public String getName() {
			return "cy_add";
		}
	}
	@Test(description = "自定义方法")
	public void t28() {
		AviatorEvaluator.addFunction(new AddFunction());
		System.out.println(AviatorEvaluator.execute("cy_add(1, 2)"));           // 3.0
		System.out.println(AviatorEvaluator.execute("cy_add(cy_add(1, 2), 100)")); // 103.0
	}

	@Test(description = "调用 Java 静态方法")
	public void t29() throws NoSuchMethodException, IllegalAccessException {
		String el="use org.apache.commons.lang3.StringUtils; if(StringUtils.isBlank(' ')){p(1)};";
		AviatorEvaluator.execute(el);

		AviatorEvaluator.addStaticFunctions("cyStringUtils", StringUtils.class);
		System.out.println(AviatorEvaluator.execute("cyStringUtils.isBlank(a)"));
	}

	@Test(description = "调用 Java 实例方法")
	public void t30() throws NoSuchMethodException, IllegalAccessException {
		AviatorEvaluator.addInstanceFunctions("s", String.class);
		System.out.println(AviatorEvaluator.execute("s.indexOf('hello', 'l')"));
	}

	@Test(description = "数组和集合")
	public void t31() {
		//https://www.yuque.com/boyan-avfmj/aviatorscript/ban32m
	}

	@Test(description = "DEBUG")
	public void t32(){
		AviatorEvaluator.getInstance().setOption(Options.TRACE_EVAL, true);
		System.out.println(AviatorEvaluator.execute("a =1 +1;b=a+1;b"));
	}




	@Test(description = "")
	public void t() {
		AviatorEvaluator.execute("let a=string.length(s);p(a);");
	}

}
